﻿using System;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using OpenCvSharp;
using FLANN;

namespace TemplateModel
{
    public class Template
    {
        private static Mat Mask;
        private static Mat MouseImage;
        public Mat MakeTemplate(Mat image)
        {
            
            var origin = image.Clone();
            Cv2.Resize(origin, origin, new Size(1224, 1024));
            // 初始化mask
            Mask = new Mat(image.Size(), MatType.CV_8UC1);
            Mask.SetTo(new Scalar((double) GrabCutClasses.BGD));
            
            // 筛选区域
            var rect = Cv2.SelectROI(origin);
            rect = RectNormalize(rect, new Size(1224, 1024), new Size(2448, 2048));
            Mat show = image.Clone();
            show.Rectangle(rect, Scalar.Red);
            Cv2.Resize(show, show, new Size(1224, 1024));
            var win = new Window("roi");
            Cv2.ImShow("roi", show);

            MouseImage = show.Clone();
            var target = new Mat(Mask, rect);
            target.SetTo(new Scalar((double) (GrabCutClasses.PR_FGD)));
            win.SetMouseCallback(draw);
            while (true)
            {
                int ch = Cv2.WaitKey(1);
                if(ch =='q') break;
                Cv2.ImShow("roi",MouseImage);
            }
            
            // 寻找轮廓
            
            Mat bgdModel = new Mat();
            Mat fgdModel = new Mat();
            Cv2.GrabCut(image, Mask, rect, bgdModel, fgdModel, 2, GrabCutModes.Eval);


            Mat binary_mask = new Mat(Mask.Rows, Mask.Cols, MatType.CV_8UC1);
            binary_mask = Mask & 1;
            Mat result = new Mat();
            image.CopyTo(result, binary_mask);
            // Cv2.ImShow("result", result);


            // 二值化
            target = result.CvtColor(ColorConversionCodes.BGR2GRAY);
            Mat binary = new Mat();
            Cv2.Threshold(target, binary, 1, 255, ThresholdTypes.Otsu);
            Mat show_binary = binary.Resize(new Size(1224, 1024));
            // Cv2.ImShow("binary", show_binary);

            // 膨胀连接轮廓
            Mat dilate_image = new Mat();
            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3,3));
            Cv2.Dilate(binary,dilate_image,element);
            // Cv2.ImShow("dialate",dilate_image);
            
            // 轮廓搜索
            Cv2.FindContours(dilate_image, out Point[][] contours, out HierarchyIndex[] hierarchy,
                RetrievalModes.External, ContourApproximationModes.ApproxNone
            );
            
            // 
            var box = Cv2.MinAreaRect(contours[0]);
            // var bounding = Cv2.BoundingRect(contours[0]);
            Cv2.DrawContours(image,contours,0,Scalar.Red);
            // Cv2.ImShow("origin",origin);
            
            var standrad_rectange = GeometryFeatureModule.StandardizeRotatedRectangle(box);
            
            // 按正方形裁剪
            var length = standrad_rectange.Length;
            var center = standrad_rectange.Center;
            Console.WriteLine(length);
            var d = Math.Sqrt(length * length*2);
            
            var bounding = new Rect((int)(center.X - d / 2.0f), (int)(center.Y - d / 2.0f), (int)d,(int) d);
            // 扩充候选区域
            var template = new Mat(image, bounding);
            Console.WriteLine(d);
            var rotated = ImageRotate(template, -standrad_rectange.Angle);
            // Mat show_rotate = new Mat();
            // Cv2.Resize(rotated,show_rotate,new Size(612,512));
            // Cv2.ImShow("Rotate",show_rotate);

            var new_rect = new Rect((int)(template.Width/2-length/2),(int)(template.Height/2-length/2),(int)length,(int)length);
            Console.WriteLine(length);
            // var tmp = rotated.Clone();
            // Cv2.ImShow("tmp",tmp);
            result = new Mat(rotated, new_rect);
            Cv2.ImShow("finnal",result);
            return result;

        }

        /// <summary>
        /// 图像膨胀操作
        /// </summary>
        static Mat ImageExpand(Mat image, int size)
        {
            Mat dilate_image = new Mat();
            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(size,size));
            Cv2.Dilate(image,dilate_image,element);
            return dilate_image;
        }

        /// <summary>
        /// 将缩放后的角度归一化转化为原图矩形框
        /// </summary>
        static Rect RectNormalize(Rect rect, Size origin_size, Size target_size)
        {
            var width_scale = (float)target_size.Width /(float) origin_size.Width;
            var height_scale = (float)target_size.Height / (float)origin_size.Height;
            
            var new_rect = new Rect((int) (rect.Left *width_scale), (int) (rect.Top *height_scale),
                (int) (rect.Width *width_scale), (int) (rect.Height *height_scale));
            return new_rect;
        }
        
        
        /// <summary>
        ///  获取分割后图像的旋转矩形
        /// </summary>
        static RotatedRect ImageRotatedRect(Mat image)
        {
            Mat binary = new Mat();
            Mat gray = image.CvtColor(ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(gray,binary, 1, 255, ThresholdTypes.Binary);
            Cv2.FindContours(binary,out Point[][] contours,out HierarchyIndex[] hierarchy,
                RetrievalModes.External,ContourApproximationModes.ApproxNone);
            var box = Cv2.MinAreaRect(contours[0]);
            return box;
        }
        
        /// <summary>
        /// 以图像中心为旋转轴旋转指定角度
        /// </summary>
        static Mat ImageRotate(Mat image, double angle)
        {
            Mat new_image = new Mat();
            Point2f center = new Point2f((float)image.Cols/2,(float)image.Rows/2);
            Mat r = Cv2.GetRotationMatrix2D(center, angle, 1.0);
            Cv2.WarpAffine(image,new_image,r,image.Size());
            return new_image;
        }

        Point PointNormalize(Point origin_point, Size origin_size, Size target_size)
        {
            var width_scale = (float)target_size.Width /(float) origin_size.Width;
            var height_scale = (float)target_size.Height / (float)origin_size.Height;

            var result = new Point(origin_point.X * width_scale, 
                                    origin_point.Y * height_scale);
            return result;
        }
        
        public void draw(MouseEventTypes event_types,int x,int y,MouseEventFlags flags,IntPtr user_data)
        {
            if (event_types == MouseEventTypes.LButtonDown)
            {
                Cv2.Circle(MouseImage,new Point(x,y),2,Scalar.Red,-1);
                var postion = PointNormalize(new Point(x, y),
                    new Size(1224, 1024),
                    new Size(2448, 2048));
                Mask.Set(postion.Y,postion.X,1);
            }

            if (event_types == MouseEventTypes.RButtonDown)
            {
                Cv2.Circle(MouseImage,new Point(x,y),2,Scalar.Green,-1);
                var postion = PointNormalize(new Point(x, y),
                    new Size(1224, 1024),
                    new Size(2448, 2048));
                Mask.Set(postion.Y,postion.X,0);
            }
        }
    }
}